// SPDX-License-Identifier: GPL-2.0+
/*
 * OX03C10 camera sensor driver library. The sensor is usually paired with a serializer device.
 *
 * Copyright 2024 NXP
 *
 */

#include <linux/i2c.h>
#include <linux/regmap.h>

#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>

#include <uapi/linux/ox03c10.h>

#include "ox03c10.h"
#include "ox03c10_regs.h"

#define OX03C10_I2C_ADDR		0x36
#define OX03C10_PIXEL_RATE		90000000

#define OX03C10_PWL_LUT_SIZE		132

static const struct ox03c10_reg {
	u16	addr;
	u8	val;
} ox03c10_init_data[] = {
	{ 0x4d5a, 0x1a }, { 0x4d09, 0xff }, { 0x4d09, 0xdf }, { 0x0301, 0xc8 }, { 0x0303, 0x01 },
	{ 0x0304, 0x01 }, { 0x0305, 0x2c }, { 0x0306, 0x04 }, { 0x0307, 0x01 }, { 0x0316, 0x00 },
	{ 0x0317, 0x00 }, { 0x0318, 0x00 }, { 0x0323, 0x05 }, { 0x0324, 0x01 }, { 0x0325, 0x2c },
	{ 0x0400, 0xe0 }, { 0x0401, 0x80 }, { 0x0403, 0xde }, { 0x0404, 0x34 }, { 0x0405, 0x3b },
	{ 0x0406, 0xde }, { 0x0407, 0x08 }, { 0x0408, 0xe0 }, { 0x0409, 0x7f }, { 0x040a, 0xde },
	{ 0x040b, 0x34 }, { 0x040c, 0x47 }, { 0x040d, 0xd8 }, { 0x040e, 0x08 }, { 0x2803, 0xfe },
	{ 0x280b, 0x00 }, { 0x280c, 0x79 }, { 0x3001, 0x03 }, { 0x3002, 0xf8 }, { 0x3005, 0x80 },
	{ 0x3007, 0x01 }, { 0x3008, 0x80 }, { 0x3012, 0x41 }, { 0x3020, 0x05 }, { 0x3700, 0x28 },
	{ 0x3701, 0x15 }, { 0x3702, 0x19 }, { 0x3703, 0x23 }, { 0x3704, 0x0a }, { 0x3705, 0x00 },
	{ 0x3706, 0x3e }, { 0x3707, 0x0d }, { 0x3708, 0x50 }, { 0x3709, 0x5a }, { 0x370a, 0x00 },
	{ 0x370b, 0x96 }, { 0x3711, 0x11 }, { 0x3712, 0x13 }, { 0x3717, 0x02 }, { 0x3718, 0x73 },
	{ 0x372c, 0x40 }, { 0x3733, 0x01 }, { 0x3738, 0x36 }, { 0x3739, 0x36 }, { 0x373a, 0x25 },
	{ 0x373b, 0x25 }, { 0x373f, 0x21 }, { 0x3740, 0x21 }, { 0x3741, 0x21 }, { 0x3742, 0x21 },
	{ 0x3747, 0x28 }, { 0x3748, 0x28 }, { 0x3749, 0x19 }, { 0x3755, 0x1a }, { 0x3756, 0x0a },
	{ 0x3757, 0x1c }, { 0x3765, 0x19 }, { 0x3766, 0x05 }, { 0x3767, 0x05 }, { 0x3768, 0x13 },
	{ 0x376c, 0x07 }, { 0x3778, 0x20 }, { 0x377c, 0xc8 }, { 0x3781, 0x02 }, { 0x3783, 0x02 },
	{ 0x379c, 0x58 }, { 0x379e, 0x00 }, { 0x379f, 0x00 }, { 0x37a0, 0x00 }, { 0x37bc, 0x22 },
	{ 0x37c0, 0x01 }, { 0x37c4, 0x3e }, { 0x37c5, 0x3e }, { 0x37c6, 0x2a }, { 0x37c7, 0x28 },
	{ 0x37c8, 0x02 }, { 0x37c9, 0x12 }, { 0x37cb, 0x29 }, { 0x37cd, 0x29 }, { 0x37d2, 0x00 },
	{ 0x37d3, 0x73 }, { 0x37d6, 0x00 }, { 0x37d7, 0x6b }, { 0x37dc, 0x00 }, { 0x37df, 0x54 },
	{ 0x37e2, 0x00 }, { 0x37e3, 0x00 }, { 0x37f8, 0x00 }, { 0x37f9, 0x01 }, { 0x37fa, 0x00 },
	{ 0x37fb, 0x19 }, { 0x3c03, 0x01 }, { 0x3c04, 0x01 }, { 0x3c06, 0x21 }, { 0x3c08, 0x01 },
	{ 0x3c09, 0x01 }, { 0x3c0a, 0x01 }, { 0x3c0b, 0x21 }, { 0x3c13, 0x21 }, { 0x3c14, 0x82 },
	{ 0x3c16, 0x13 }, { 0x3c21, 0x00 }, { 0x3c22, 0xf3 }, { 0x3c37, 0x12 }, { 0x3c38, 0x31 },
	{ 0x3c3c, 0x00 }, { 0x3c3d, 0x03 }, { 0x3c44, 0x16 }, { 0x3c5c, 0x8a }, { 0x3c5f, 0x03 },
	{ 0x3c61, 0x80 }, { 0x3c6f, 0x2b }, { 0x3c70, 0x5f }, { 0x3c71, 0x2c }, { 0x3c72, 0x2c },
	{ 0x3c73, 0x2c }, { 0x3c76, 0x12 }, { 0x3182, 0x12 }, { 0x320e, 0x00 }, { 0x320f, 0x00 },
	{ 0x3211, 0x61 }, { 0x3215, 0xcd }, { 0x3219, 0x08 }, { 0x3506, 0x30 }, { 0x350a, 0x01 },
	{ 0x350b, 0x00 }, { 0x350c, 0x00 }, { 0x3586, 0x60 }, { 0x358a, 0x01 }, { 0x358b, 0x00 },
	{ 0x358c, 0x00 }, { 0x3541, 0x00 }, { 0x3542, 0x04 }, { 0x3548, 0x04 }, { 0x3549, 0x40 },
	{ 0x354a, 0x01 }, { 0x354b, 0x00 }, { 0x354c, 0x00 }, { 0x35c1, 0x00 }, { 0x35c2, 0x02 },
	{ 0x35c6, 0xa0 }, { 0x3600, 0x8f }, { 0x3605, 0x16 }, { 0x3609, 0xf0 }, { 0x360a, 0x01 },
	{ 0x360e, 0x1d }, { 0x360f, 0x10 }, { 0x3610, 0x70 }, { 0x3611, 0x3a }, { 0x3612, 0x28 },
	{ 0x361a, 0x29 }, { 0x361b, 0x6c }, { 0x361c, 0x0b }, { 0x361d, 0x00 }, { 0x361e, 0xfc },
	{ 0x362a, 0x00 }, { 0x364d, 0x0f }, { 0x364e, 0x18 }, { 0x364f, 0x12 }, { 0x3653, 0x1c },
	{ 0x3654, 0x00 }, { 0x3655, 0x1f }, { 0x3656, 0x1f }, { 0x3657, 0x0c }, { 0x3658, 0x0a },
	{ 0x3659, 0x14 }, { 0x365a, 0x18 }, { 0x365b, 0x14 }, { 0x365c, 0x10 }, { 0x365e, 0x12 },
	{ 0x3674, 0x08 }, { 0x3677, 0x3a }, { 0x3678, 0x3a }, { 0x3679, 0x19 }, { 0x3802, 0x00 },
	{ 0x3803, 0x04 }, { 0x3806, 0x05 }, { 0x3807, 0x0b }, { 0x3808, 0x07 }, { 0x3809, 0x80 },
	{ 0x380a, 0x05 }, { 0x380b, 0x00 }, { 0x380c, 0x04 }, { 0x380d, 0x47 }, { 0x380e, 0x02 },
	{ 0x380f, 0xae }, { 0x3810, 0x00 }, { 0x3811, 0x08 }, { 0x3812, 0x00 }, { 0x3813, 0x04 },
	{ 0x3816, 0x01 }, { 0x3817, 0x01 }, { 0x381c, 0x18 }, { 0x381e, 0x01 }, { 0x381f, 0x01 },
	{ 0x3820, 0x20 }, { 0x3821, 0x19 }, { 0x3832, 0x00 }, { 0x3834, 0x00 }, { 0x384c, 0x02 },
	{ 0x384d, 0x0d }, { 0x3850, 0x00 }, { 0x3851, 0x42 }, { 0x3852, 0x00 }, { 0x3853, 0x40 },
	{ 0x3858, 0x04 }, { 0x388c, 0x02 }, { 0x388d, 0x2b }, { 0x3b40, 0x05 }, { 0x3b41, 0x40 },
	{ 0x3b42, 0x00 }, { 0x3b43, 0x90 }, { 0x3b44, 0x00 }, { 0x3b45, 0x20 }, { 0x3b46, 0x00 },
	{ 0x3b47, 0x20 }, { 0x3b48, 0x19 }, { 0x3b49, 0x12 }, { 0x3b4a, 0x16 }, { 0x3b4b, 0x2e },
	{ 0x3b4c, 0x00 }, { 0x3b4d, 0x00 }, { 0x3b86, 0x00 }, { 0x3b87, 0x34 }, { 0x3b88, 0x00 },
	{ 0x3b89, 0x08 }, { 0x3b8a, 0x05 }, { 0x3b8b, 0x00 }, { 0x3b8c, 0x07 }, { 0x3b8d, 0x80 },
	{ 0x3b8e, 0x00 }, { 0x3b8f, 0x00 }, { 0x3b92, 0x05 }, { 0x3b93, 0x00 }, { 0x3b94, 0x07 },
	{ 0x3b95, 0x80 }, { 0x3b9e, 0x09 }, { 0x3d82, 0x73 }, { 0x3d85, 0x05 }, { 0x3d8a, 0x03 },
	{ 0x3d8b, 0xff }, { 0x3d99, 0x00 }, { 0x3d9a, 0x9f }, { 0x3d9b, 0x00 }, { 0x3d9c, 0xa0 },
	{ 0x3da4, 0x00 }, { 0x3da7, 0x50 }, { 0x420e, 0xff }, { 0x420f, 0xff }, { 0x4210, 0xff },
	{ 0x4211, 0xff }, { 0x421e, 0x02 }, { 0x421f, 0x45 }, { 0x4220, 0xe1 }, { 0x4221, 0x05 },
	{ 0x4301, 0x0f }, { 0x4307, 0x03 }, { 0x4308, 0x13 }, { 0x430a, 0x13 }, { 0x430d, 0x93 },
	{ 0x430e, 0x14 }, { 0x430f, 0x17 }, { 0x4310, 0x95 }, { 0x4311, 0x16 }, { 0x4316, 0x00 },
	{ 0x4317, 0x28 }, { 0x4319, 0x01 }, { 0x431a, 0x00 }, { 0x431b, 0x00 }, { 0x431d, 0x2a },
	{ 0x431e, 0x11 }, { 0x431f, 0x30 }, { 0x4320, 0x19 }, { 0x4323, 0x80 }, { 0x4324, 0x00 },
	{ 0x4503, 0x4e }, { 0x4505, 0x00 }, { 0x4509, 0x00 }, { 0x450a, 0x00 }, { 0x4580, 0xf8 },
	{ 0x4583, 0x07 }, { 0x4584, 0x6a }, { 0x4585, 0x08 }, { 0x4586, 0x05 }, { 0x4587, 0x04 },
	{ 0x4588, 0x73 }, { 0x4589, 0x05 }, { 0x458a, 0x1f }, { 0x458b, 0x02 }, { 0x458c, 0xdc },
	{ 0x458d, 0x03 }, { 0x458e, 0x02 }, { 0x4597, 0x07 }, { 0x4598, 0x40 }, { 0x4599, 0x0e },
	{ 0x459a, 0x0e }, { 0x459b, 0xfb }, { 0x459c, 0xf3 }, { 0x4602, 0x00 }, { 0x4603, 0x13 },
	{ 0x4604, 0x00 }, { 0x4609, 0x0a }, { 0x460a, 0x30 }, { 0x4610, 0x00 }, { 0x4611, 0x70 },
	{ 0x4612, 0x01 }, { 0x4613, 0x00 }, { 0x4614, 0x00 }, { 0x4615, 0x70 }, { 0x4616, 0x01 },
	{ 0x4617, 0x00 }, { 0x4800, 0x04 }, { 0x480a, 0x22 }, { 0x4813, 0xe4 }, { 0x4814, 0x2a },
	{ 0x4837, 0x0d }, { 0x484b, 0x47 }, { 0x484f, 0x00 }, { 0x4887, 0x51 }, { 0x4d00, 0x4a },
	{ 0x4d01, 0x18 }, { 0x4d05, 0xff }, { 0x4d06, 0x88 }, { 0x4d08, 0x63 }, { 0x4d09, 0xdf },
	{ 0x4d15, 0x7d }, { 0x4d1a, 0x20 }, { 0x4d30, 0x0a }, { 0x4d31, 0x00 }, { 0x4d34, 0x7d },
	{ 0x4d3c, 0x7d }, { 0x4f00, 0x3f }, { 0x4f01, 0xff }, { 0x4f02, 0xff }, { 0x4f03, 0x2c },
	{ 0x4f04, 0xe0 }, { 0x6a00, 0x00 }, { 0x6a01, 0x20 }, { 0x6a02, 0x00 }, { 0x6a03, 0x20 },
	{ 0x6a04, 0x02 }, { 0x6a05, 0x80 }, { 0x6a06, 0x01 }, { 0x6a07, 0xe0 }, { 0x6a08, 0xcf },
	{ 0x6a09, 0x01 }, { 0x6a0a, 0x40 }, { 0x6a20, 0x00 }, { 0x6a21, 0x02 }, { 0x6a22, 0x00 },
	{ 0x6a23, 0x00 }, { 0x6a24, 0x00 }, { 0x6a25, 0x00 }, { 0x6a26, 0x00 }, { 0x6a27, 0x00 },
	{ 0x6a28, 0x00 }, { 0x5000, 0x8f }, { 0x5001, 0x65 }, { 0x5002, 0x60 }, { 0x5003, 0x6a },
	{ 0x5004, 0x3e }, { 0x5005, 0x1e }, { 0x5006, 0x1e }, { 0x5007, 0x1e }, { 0x5008, 0x00 },
	{ 0x500c, 0x00 }, { 0x502c, 0x00 }, { 0x502e, 0x00 }, { 0x502f, 0x00 }, { 0x504b, 0x00 },
	{ 0x5053, 0x00 }, { 0x505b, 0x00 }, { 0x5063, 0x00 }, { 0x5070, 0x00 }, { 0x5074, 0x04 },
	{ 0x507a, 0x00 }, { 0x507b, 0x00 }, { 0x5500, 0x02 }, { 0x5700, 0x02 }, { 0x5900, 0x02 },
	{ 0x6007, 0x04 }, { 0x6008, 0x05 }, { 0x6009, 0x02 }, { 0x600b, 0x08 }, { 0x600c, 0x07 },
	{ 0x600d, 0x88 }, { 0x6016, 0x00 }, { 0x6027, 0x04 }, { 0x6028, 0x05 }, { 0x6029, 0x02 },
	{ 0x602b, 0x08 }, { 0x602c, 0x07 }, { 0x602d, 0x88 }, { 0x6047, 0x04 }, { 0x6048, 0x05 },
	{ 0x6049, 0x02 }, { 0x604b, 0x08 }, { 0x604c, 0x07 }, { 0x604d, 0x88 }, { 0x6067, 0x04 },
	{ 0x6068, 0x05 }, { 0x6069, 0x02 }, { 0x606b, 0x08 }, { 0x606c, 0x07 }, { 0x606d, 0x88 },
	{ 0x6087, 0x04 }, { 0x6088, 0x05 }, { 0x6089, 0x02 }, { 0x608b, 0x08 }, { 0x608c, 0x07 },
	{ 0x608d, 0x88 }, { 0x5e00, 0x02 }, { 0x5E01, 0x0F }, { 0x5E02, 0x0F }, { 0x5E03, 0x10 },
	{ 0x5E04, 0x11 }, { 0x5E05, 0x12 }, { 0x5E06, 0x13 }, { 0x5E07, 0x00 }, { 0x5E08, 0x00 },
	{ 0x5E09, 0x00 }, { 0x5E0A, 0x00 }, { 0x5E0B, 0x00 }, { 0x5E0C, 0x00 }, { 0x5E0D, 0x00 },
	{ 0x5E0E, 0x00 }, { 0x5E0F, 0x00 }, { 0x5E10, 0x00 }, { 0x5E11, 0x00 }, { 0x5E12, 0x00 },
	{ 0x5E13, 0x00 }, { 0x5E14, 0x00 }, { 0x5E15, 0x00 }, { 0x5E16, 0x00 }, { 0x5E17, 0x00 },
	{ 0x5E18, 0x00 }, { 0x5E19, 0x00 }, { 0x5E1A, 0x00 }, { 0x5E1B, 0x00 }, { 0x5E1C, 0x00 },
	{ 0x5E1D, 0x00 }, { 0x5E1E, 0x00 }, { 0x5E1F, 0x00 }, { 0x5E20, 0x00 }, { 0x5E21, 0x00 },
	{ 0x5E22, 0x00 }, { 0x5E23, 0x7f }, { 0x5E24, 0xff }, { 0x5E25, 0x00 }, { 0x5E26, 0x40 },
	{ 0x5E27, 0x00 }, { 0x5E28, 0x00 }, { 0x5E29, 0x20 }, { 0x5E2A, 0x00 }, { 0x5E2B, 0x00 },
	{ 0x5E2C, 0x04 }, { 0x5E2D, 0x92 }, { 0x5E2E, 0x00 }, { 0x5E2F, 0x09 }, { 0x5E30, 0x25 },
	{ 0x5E31, 0x00 }, { 0x5E32, 0x12 }, { 0x5E33, 0x49 }, { 0x5E34, 0x00 }, { 0x5E35, 0x00 },
	{ 0x5E36, 0x00 }, { 0x5E37, 0x00 }, { 0x5E38, 0x00 }, { 0x5E39, 0x00 }, { 0x5E3A, 0x00 },
	{ 0x5E3B, 0x00 }, { 0x5E3C, 0x00 }, { 0x5E3D, 0x00 }, { 0x5E3E, 0x00 }, { 0x5E3F, 0x00 },
	{ 0x5E40, 0x00 }, { 0x5E41, 0x00 }, { 0x5E42, 0x00 }, { 0x5E43, 0x00 }, { 0x5E44, 0x00 },
	{ 0x5E45, 0x00 }, { 0x5E46, 0x00 }, { 0x5E47, 0x00 }, { 0x5E48, 0x00 }, { 0x5E49, 0x00 },
	{ 0x5E4A, 0x00 }, { 0x5E4B, 0x00 }, { 0x5E4C, 0x00 }, { 0x5E4D, 0x00 }, { 0x5E4E, 0x00 },
	{ 0x5E4F, 0x00 }, { 0x5E50, 0x00 }, { 0x5E51, 0x00 }, { 0x5E52, 0x00 }, { 0x5E53, 0x00 },
	{ 0x5E54, 0x00 }, { 0x5E55, 0x00 }, { 0x5E56, 0x00 }, { 0x5E57, 0x00 }, { 0x5E58, 0x00 },
	{ 0x5E59, 0x00 }, { 0x5E5A, 0x00 }, { 0x5E5B, 0x00 }, { 0x5E5C, 0x00 }, { 0x5E5D, 0x00 },
	{ 0x5E5E, 0x00 }, { 0x5E5F, 0x00 }, { 0x5E60, 0x00 }, { 0x5E61, 0x00 }, { 0x5E62, 0x00 },
	{ 0x5E63, 0x00 }, { 0x5E64, 0x00 }, { 0x5E65, 0x00 }, { 0x5E66, 0x00 }, { 0x5E67, 0x00 },
	{ 0x5E68, 0x00 }, { 0x5E69, 0x00 }, { 0x5E6A, 0x00 }, { 0x5E6B, 0x00 }, { 0x5E6C, 0x00 },
	{ 0x5E6D, 0x00 }, { 0x5E6E, 0x00 }, { 0x5E6F, 0x00 }, { 0x5E70, 0x00 }, { 0x5E71, 0x00 },
	{ 0x5E72, 0x00 }, { 0x5E73, 0x00 }, { 0x5E74, 0x00 }, { 0x5E75, 0x00 }, { 0x5E76, 0x00 },
	{ 0x5E77, 0x00 }, { 0x5E78, 0x00 }, { 0x5E79, 0x00 }, { 0x5E7A, 0x00 }, { 0x5E7B, 0x00 },
	{ 0x5E7C, 0x00 }, { 0x5E7D, 0x00 }, { 0x5E7E, 0x00 }, { 0x5E7F, 0x00 }, { 0x5E80, 0x00 },
	{ 0x5E81, 0x00 }, { 0x5E82, 0x00 }, { 0x5E83, 0x00 }, { 0x5E84, 0x00 }, { 0x5f00, 0x02 },
	{ 0x5f01, 0x08 }, { 0x5f02, 0x09 }, { 0x5f03, 0x0a }, { 0x5f04, 0x0b }, { 0x5f05, 0x0c },
	{ 0x5f06, 0x0c }, { 0x5f07, 0x0c }, { 0x5f08, 0x0c }, { 0x5f09, 0x0c }, { 0x5f0a, 0x0d },
	{ 0x5f0b, 0x0d }, { 0x5f0c, 0x0d }, { 0x5f0d, 0x0d }, { 0x5f0e, 0x0d }, { 0x5f0f, 0x0e },
	{ 0x5f10, 0x0e }, { 0x5f11, 0x0e }, { 0x5f12, 0x0e }, { 0x5f13, 0x0f }, { 0x5f14, 0x0f },
	{ 0x5f15, 0x10 }, { 0x5f16, 0x11 }, { 0x5f17, 0x11 }, { 0x5f18, 0x12 }, { 0x5f19, 0x12 },
	{ 0x5f1a, 0x13 }, { 0x5f1b, 0x13 }, { 0x5f1c, 0x14 }, { 0x5f1d, 0x14 }, { 0x5f1e, 0x16 },
	{ 0x5f1f, 0x16 }, { 0x5f20, 0x16 }, { 0x5f21, 0x08 }, { 0x5f22, 0x00 }, { 0x5f23, 0x01 },
	{ 0x5f26, 0x02 }, { 0x5f27, 0x00 }, { 0x5f29, 0x02 }, { 0x5f2a, 0x00 }, { 0x5f2c, 0x02 },
	{ 0x5f2d, 0x00 }, { 0x5f2f, 0x02 }, { 0x5f30, 0x00 }, { 0x5f32, 0x02 }, { 0x5f33, 0x00 },
	{ 0x5f34, 0x00 }, { 0x5f35, 0x02 }, { 0x5f36, 0x00 }, { 0x5f37, 0x00 }, { 0x5f38, 0x02 },
	{ 0x5f39, 0x00 }, { 0x5f3a, 0x00 }, { 0x5f3b, 0x02 }, { 0x5f3c, 0x00 }, { 0x5f3d, 0x00 },
	{ 0x5f3e, 0x02 }, { 0x5f3f, 0x00 }, { 0x5f40, 0x00 }, { 0x5f41, 0x02 }, { 0x5f42, 0x00 },
	{ 0x5f43, 0x00 }, { 0x5f44, 0x02 }, { 0x5f45, 0x00 }, { 0x5f46, 0x00 }, { 0x5f47, 0x04 },
	{ 0x5f48, 0x00 }, { 0x5f49, 0x00 }, { 0x5f4a, 0x04 }, { 0x5f4b, 0x00 }, { 0x5f4c, 0x00 },
	{ 0x5f4d, 0x04 }, { 0x5f4e, 0x00 }, { 0x5f50, 0x04 }, { 0x5f51, 0x00 }, { 0x5f53, 0x04 },
	{ 0x5f54, 0x00 }, { 0x5f56, 0x04 }, { 0x5f57, 0x00 }, { 0x5f59, 0x04 }, { 0x5f5a, 0x00 },
	{ 0x5f5c, 0x04 }, { 0x5f5d, 0x00 }, { 0x5f5f, 0x08 }, { 0x5f60, 0x00 }, { 0x5f62, 0x08 },
	{ 0x5f63, 0x00 }, { 0x5f65, 0x08 }, { 0x5f66, 0x00 }, { 0x5f68, 0x08 }, { 0x5f69, 0x00 },
	{ 0x5f6b, 0x08 }, { 0x5f6c, 0x00 }, { 0x5f6e, 0x10 }, { 0x5f6f, 0x00 }, { 0x5f71, 0x10 },
	{ 0x5f72, 0x00 }, { 0x5f74, 0x10 }, { 0x5f75, 0x00 }, { 0x5f77, 0x10 }, { 0x5f78, 0x00 },
	{ 0x5f7a, 0x20 }, { 0x5f7b, 0x00 }, { 0x5f7d, 0x20 }, { 0x5f7e, 0x00 }, { 0x5f80, 0x20 },
	{ 0x5f81, 0x00 }, { 0x5f83, 0x00 }, { 0x5f84, 0xff }, { 0x5240, 0x0f }, { 0x5243, 0x00 },
	{ 0x5244, 0x00 }, { 0x5245, 0x00 }, { 0x5246, 0x00 }, { 0x5247, 0x00 }, { 0x5248, 0x00 },
	{ 0x5249, 0x00 }, { 0x5440, 0x0f }, { 0x5443, 0x00 }, { 0x5445, 0x00 }, { 0x5447, 0x00 },
	{ 0x5448, 0x00 }, { 0x5449, 0x00 }, { 0x5640, 0x0f }, { 0x5642, 0x00 }, { 0x5643, 0x00 },
	{ 0x5644, 0x00 }, { 0x5645, 0x00 }, { 0x5646, 0x00 }, { 0x5647, 0x00 }, { 0x5649, 0x00 },
	{ 0x5840, 0x0f }, { 0x5842, 0x00 }, { 0x5843, 0x00 }, { 0x5845, 0x00 }, { 0x5846, 0x00 },
	{ 0x5847, 0x00 }, { 0x5848, 0x00 }, { 0x5849, 0x00 }, { 0x4001, 0x2b }, { 0x4008, 0x02 },
	{ 0x4009, 0x03 }, { 0x4018, 0x12 }, { 0x4022, 0x40 }, { 0x4023, 0x20 }, { 0x4026, 0x00 },
	{ 0x4027, 0x40 }, { 0x4028, 0x00 }, { 0x4029, 0x40 }, { 0x402a, 0x00 }, { 0x402b, 0x40 },
	{ 0x402c, 0x00 }, { 0x402d, 0x40 }, { 0x405e, 0x00 }, { 0x405f, 0x00 }, { 0x4060, 0x00 },
	{ 0x4061, 0x00 }, { 0x4062, 0x00 }, { 0x4063, 0x00 }, { 0x4064, 0x00 }, { 0x4065, 0x00 },
	{ 0x4066, 0x00 }, { 0x4067, 0x00 }, { 0x4068, 0x00 }, { 0x4069, 0x00 }, { 0x406a, 0x00 },
	{ 0x406b, 0x00 }, { 0x406c, 0x00 }, { 0x406d, 0x00 }, { 0x406e, 0x00 }, { 0x406f, 0x00 },
	{ 0x4070, 0x00 }, { 0x4071, 0x00 }, { 0x4072, 0x00 }, { 0x4073, 0x00 }, { 0x4074, 0x00 },
	{ 0x4075, 0x00 }, { 0x4076, 0x00 }, { 0x4077, 0x00 }, { 0x4078, 0x00 }, { 0x4079, 0x00 },
	{ 0x407a, 0x00 }, { 0x407b, 0x00 }, { 0x407c, 0x00 }, { 0x407d, 0x00 }, { 0x407e, 0xcc },
	{ 0x407f, 0x18 }, { 0x4080, 0xff }, { 0x4081, 0xff }, { 0x4082, 0x01 }, { 0x4083, 0x53 },
	{ 0x4084, 0x01 }, { 0x4085, 0x2b }, { 0x4086, 0x00 }, { 0x4087, 0xb3 }, { 0x4640, 0x40 },
	{ 0x4641, 0x11 }, { 0x4642, 0x0e }, { 0x4643, 0xee }, { 0x4646, 0x0f }, { 0x4648, 0x00 },
	{ 0x4649, 0x03 }, { 0x4f04, 0xf8 }, { 0x4d09, 0xff }, { 0x4d09, 0xdf }, { 0x5019, 0x00 },
	{ 0x501a, 0xff }, { 0x501b, 0xff }, { 0x501d, 0x00 }, { 0x501e, 0x23 }, { 0x501f, 0x8e },
	{ 0x5021, 0x00 }, { 0x5022, 0x00 }, { 0x5023, 0x50 }, { 0x5025, 0x00 }, { 0x5026, 0x23 },
	{ 0x5027, 0x8e }, { 0x5003, 0x7a }, { 0x5b80, 0x08 }, { 0x5c00, 0x08 }, { 0x5c80, 0x00 },
	{ 0x5bbe, 0x12 }, { 0x5c3e, 0x12 }, { 0x5cbe, 0x12 }, { 0x5b8a, 0x80 }, { 0x5b8b, 0x80 },
	{ 0x5b8c, 0x80 }, { 0x5b8d, 0x80 }, { 0x5b8e, 0x80 }, { 0x5b8f, 0x40 }, { 0x5b90, 0x80 },
	{ 0x5b91, 0x80 }, { 0x5b92, 0x80 }, { 0x5b93, 0x60 }, { 0x5b94, 0x00 }, { 0x5b95, 0x00 },
	{ 0x5b96, 0x40 }, { 0x5b97, 0x80 }, { 0x5b98, 0x10 }, { 0x5b99, 0x00 }, { 0x5b9a, 0x00 },
	{ 0x5b9b, 0x00 }, { 0x5b9c, 0x00 }, { 0x5b9d, 0x00 }, { 0x5b9e, 0x00 }, { 0x5b9f, 0x00 },
	{ 0x5ba0, 0x00 }, { 0x5ba1, 0x00 }, { 0x5ba2, 0x00 }, { 0x5ba3, 0x00 }, { 0x5ba4, 0x00 },
	{ 0x5ba5, 0x00 }, { 0x5ba6, 0x00 }, { 0x5ba7, 0x00 }, { 0x5ba8, 0x00 }, { 0x5ba9, 0xc0 },
	{ 0x5baa, 0x01 }, { 0x5bab, 0x40 }, { 0x5bac, 0x02 }, { 0x5bad, 0x40 }, { 0x5bae, 0x00 },
	{ 0x5baf, 0x50 }, { 0x5bb0, 0x00 }, { 0x5bb1, 0x60 }, { 0x5bb2, 0x00 }, { 0x5bb3, 0xc0 },
	{ 0x5c0a, 0x80 }, { 0x5c0b, 0x80 }, { 0x5c0c, 0x80 }, { 0x5c0d, 0x80 }, { 0x5c0e, 0x60 },
	{ 0x5c0f, 0x80 }, { 0x5c10, 0x80 }, { 0x5c11, 0x80 }, { 0x5c12, 0x60 }, { 0x5c13, 0x20 },
	{ 0x5c14, 0x80 }, { 0x5c15, 0x80 }, { 0x5c16, 0x80 }, { 0x5c17, 0x20 }, { 0x5c18, 0x00 },
	{ 0x5c19, 0x80 }, { 0x5c1a, 0x40 }, { 0x5c1b, 0x20 }, { 0x5c1c, 0x00 }, { 0x5c1d, 0x00 },
	{ 0x5c1e, 0x80 }, { 0x5c1f, 0x00 }, { 0x5c20, 0x00 }, { 0x5c21, 0x00 }, { 0x5c22, 0x00 },
	{ 0x5c23, 0x00 }, { 0x5c24, 0x00 }, { 0x5c25, 0x00 }, { 0x5c26, 0x00 }, { 0x5c27, 0x00 },
	{ 0x5c28, 0x02 }, { 0x5c29, 0x00 }, { 0x5c2a, 0x02 }, { 0x5c2b, 0x76 }, { 0x5c2c, 0x03 },
	{ 0x5c2d, 0x08 }, { 0x5c2e, 0x00 }, { 0x5c2f, 0x80 }, { 0x5c30, 0x01 }, { 0x5c31, 0x00 },
	{ 0x5c32, 0x02 }, { 0x5c33, 0x00 }, { 0x5c8a, 0x80 }, { 0x5c8b, 0x80 }, { 0x5c8c, 0x80 },
	{ 0x5c8d, 0x80 }, { 0x5c8e, 0x80 }, { 0x5c8f, 0x80 }, { 0x5c90, 0x80 }, { 0x5c91, 0x80 },
	{ 0x5c92, 0x80 }, { 0x5c93, 0x60 }, { 0x5c94, 0x80 }, { 0x5c95, 0x80 }, { 0x5c96, 0x80 },
	{ 0x5c97, 0x60 }, { 0x5c98, 0x40 }, { 0x5c99, 0x80 }, { 0x5c9a, 0x80 }, { 0x5c9b, 0x80 },
	{ 0x5c9c, 0x40 }, { 0x5c9d, 0x20 }, { 0x5c9e, 0x80 }, { 0x5c9f, 0x80 }, { 0x5ca0, 0x80 },
	{ 0x5ca1, 0x20 }, { 0x5ca2, 0x00 }, { 0x5ca3, 0x80 }, { 0x5ca4, 0x80 }, { 0x5ca5, 0x80 },
	{ 0x5ca6, 0x00 }, { 0x5ca7, 0x00 }, { 0x5ca8, 0x01 }, { 0x5ca9, 0x00 }, { 0x5caa, 0x02 },
	{ 0x5cab, 0x00 }, { 0x5cac, 0x03 }, { 0x5cad, 0x08 }, { 0x5cae, 0x01 }, { 0x5caf, 0x00 },
	{ 0x5cb0, 0x02 }, { 0x5cb1, 0x00 }, { 0x5cb2, 0x03 }, { 0x5cb3, 0x08 }, { 0x5be7, 0x80 },
	{ 0x5bc9, 0x80 }, { 0x5bca, 0x80 }, { 0x5bcb, 0x80 }, { 0x5bcc, 0x80 }, { 0x5bcd, 0x80 },
	{ 0x5bce, 0x80 }, { 0x5bcf, 0x80 }, { 0x5bd0, 0x80 }, { 0x5bd1, 0x80 }, { 0x5bd2, 0x20 },
	{ 0x5bd3, 0x80 }, { 0x5bd4, 0x40 }, { 0x5bd5, 0x20 }, { 0x5bd6, 0x00 }, { 0x5bd7, 0x00 },
	{ 0x5bd8, 0x00 }, { 0x5bd9, 0x00 }, { 0x5bda, 0x00 }, { 0x5bdb, 0x00 }, { 0x5bdc, 0x00 },
	{ 0x5bdd, 0x00 }, { 0x5bde, 0x00 }, { 0x5bdf, 0x00 }, { 0x5be0, 0x00 }, { 0x5be1, 0x00 },
	{ 0x5be2, 0x00 }, { 0x5be3, 0x00 }, { 0x5be4, 0x00 }, { 0x5be5, 0x00 }, { 0x5be6, 0x00 },
	{ 0x5c49, 0x80 }, { 0x5c4a, 0x80 }, { 0x5c4b, 0x80 }, { 0x5c4c, 0x80 }, { 0x5c4d, 0x40 },
	{ 0x5c4e, 0x80 }, { 0x5c4f, 0x80 }, { 0x5c50, 0x80 }, { 0x5c51, 0x60 }, { 0x5c52, 0x20 },
	{ 0x5c53, 0x80 }, { 0x5c54, 0x80 }, { 0x5c55, 0x80 }, { 0x5c56, 0x20 }, { 0x5c57, 0x00 },
	{ 0x5c58, 0x80 }, { 0x5c59, 0x40 }, { 0x5c5a, 0x20 }, { 0x5c5b, 0x00 }, { 0x5c5c, 0x00 },
	{ 0x5c5d, 0x80 }, { 0x5c5e, 0x00 }, { 0x5c5f, 0x00 }, { 0x5c60, 0x00 }, { 0x5c61, 0x00 },
	{ 0x5c62, 0x00 }, { 0x5c63, 0x00 }, { 0x5c64, 0x00 }, { 0x5c65, 0x00 }, { 0x5c66, 0x00 },
	{ 0x5cc9, 0x80 }, { 0x5cca, 0x80 }, { 0x5ccb, 0x80 }, { 0x5ccc, 0x80 }, { 0x5ccd, 0x80 },
	{ 0x5cce, 0x80 }, { 0x5ccf, 0x80 }, { 0x5cd0, 0x80 }, { 0x5cd1, 0x80 }, { 0x5cd2, 0x60 },
	{ 0x5cd3, 0x80 }, { 0x5cd4, 0x80 }, { 0x5cd5, 0x80 }, { 0x5cd6, 0x60 }, { 0x5cd7, 0x40 },
	{ 0x5cd8, 0x80 }, { 0x5cd9, 0x80 }, { 0x5cda, 0x80 }, { 0x5cdb, 0x40 }, { 0x5cdc, 0x20 },
	{ 0x5cdd, 0x80 }, { 0x5cde, 0x80 }, { 0x5cdf, 0x80 }, { 0x5ce0, 0x20 }, { 0x5ce1, 0x00 },
	{ 0x5ce2, 0x80 }, { 0x5ce3, 0x80 }, { 0x5ce4, 0x80 }, { 0x5ce5, 0x00 }, { 0x5ce6, 0x00 },
	{ 0x5b84, 0x02 }, { 0x5b85, 0xcc }, { 0x5bb4, 0x05 }, { 0x5bb5, 0xc6 }, { 0x5c04, 0x02 },
	{ 0x5c05, 0xcc }, { 0x5c34, 0x05 }, { 0x5c35, 0x33 }, { 0x5c84, 0x02 }, { 0x5c85, 0xcc },
	{ 0x5cb4, 0x05 }, { 0x5cb5, 0x33 }, { 0x5bbf, 0x00 }, { 0x5bc0, 0x04 }, { 0x5bc1, 0x06 },
	{ 0x5bc2, 0xff }, { 0x5bc3, 0x00 }, { 0x5bc4, 0x04 }, { 0x5bc5, 0x02 }, { 0x5bc6, 0xb8 },
	{ 0x5c3f, 0x00 }, { 0x5c40, 0x04 }, { 0x5c41, 0x07 }, { 0x5c42, 0xff }, { 0x5c43, 0x00 },
	{ 0x5c44, 0x04 }, { 0x5c45, 0x03 }, { 0x5c46, 0xb8 }, { 0x5cbf, 0x00 }, { 0x5cc0, 0x20 },
	{ 0x5cc1, 0x07 }, { 0x5cc2, 0xff }, { 0x5cc3, 0x00 }, { 0x5cc4, 0x20 }, { 0x5cc5, 0x03 },
	{ 0x5cc6, 0x00 }, { 0x5b86, 0x05 }, { 0x5c06, 0x05 }, { 0x5c86, 0x05 }, { 0x5bb8, 0x01 },
	{ 0x5bb9, 0x01 }, { 0x5c38, 0x01 }, { 0x5c39, 0x01 }, { 0x5cb8, 0x01 }, { 0x5cb9, 0x01 },
	{ 0x5bc7, 0x00 }, { 0x5bc8, 0x80 }, { 0x5c47, 0x00 }, { 0x5c48, 0x80 }, { 0x5cc7, 0x00 },
	{ 0x5cc8, 0x80 }, { 0x5bba, 0x01 }, { 0x5bbb, 0x00 }, { 0x5c3a, 0x01 }, { 0x5c3b, 0x00 },
	{ 0x5cba, 0x01 }, { 0x5cbb, 0x00 }, { 0x5d74, 0x01 }, { 0x5d75, 0x00 }, { 0x5d1f, 0x81 },
	{ 0x5d11, 0x00 }, { 0x5d12, 0x10 }, { 0x5d13, 0x10 }, { 0x5d15, 0x05 }, { 0x5d16, 0x05 },
	{ 0x5d17, 0x05 }, { 0x5d08, 0x03 }, { 0x5d09, 0x6b }, { 0x5d0a, 0x03 }, { 0x5d0b, 0x6b },
	{ 0x5d18, 0x03 }, { 0x5d19, 0x6b }, { 0x5b40, 0x01 }, { 0x5b41, 0x00 }, { 0x5b42, 0x00 },
	{ 0x5b43, 0x00 }, { 0x5b44, 0x00 }, { 0x5b45, 0x00 }, { 0x5b46, 0x00 }, { 0x5b47, 0x00 },
	{ 0x5b48, 0x01 }, { 0x5b49, 0x00 }, { 0x5b4a, 0x00 }, { 0x5b4b, 0x00 }, { 0x5b4c, 0x00 },
	{ 0x5b4d, 0x00 }, { 0x5b4e, 0x00 }, { 0x5b4f, 0x00 }, { 0x5b50, 0x01 }, { 0x5b51, 0x00 },
	{ 0x5b52, 0x01 }, { 0x5b53, 0x00 }, { 0x5b54, 0x00 }, { 0x5b55, 0x00 }, { 0x5b56, 0x00 },
	{ 0x5b57, 0x00 }, { 0x5b58, 0x00 }, { 0x5b59, 0x00 }, { 0x5b5a, 0x01 }, { 0x5b5b, 0x00 },
	{ 0x5b5c, 0x00 }, { 0x5b5d, 0x00 }, { 0x5b5e, 0x00 }, { 0x5b5f, 0x00 }, { 0x5b60, 0x00 },
	{ 0x5b61, 0x00 }, { 0x5b62, 0x01 }, { 0x5b63, 0x00 }, { 0x5b64, 0x01 }, { 0x5b65, 0x00 },
	{ 0x5b66, 0x00 }, { 0x5b67, 0x00 }, { 0x5b68, 0x00 }, { 0x5b69, 0x00 }, { 0x5b6a, 0x00 },
	{ 0x5b6b, 0x00 }, { 0x5b6c, 0x01 }, { 0x5b6d, 0x00 }, { 0x5b6e, 0x00 }, { 0x5b6f, 0x00 },
	{ 0x5b70, 0x00 }, { 0x5b71, 0x00 }, { 0x5b72, 0x00 }, { 0x5b73, 0x00 }, { 0x5b74, 0x01 },
	{ 0x5b75, 0x00 }, { 0x5b78, 0x00 }, { 0x5b79, 0x4c }, { 0x5b7a, 0x00 }, { 0x5b7b, 0xb9 },
	{ 0x5b7c, 0x01 }, { 0x5b7d, 0x38 }, { 0x5b7e, 0x01 }, { 0x5280, 0x04 }, { 0x5281, 0x00 },
	{ 0x5282, 0x04 }, { 0x5283, 0x00 }, { 0x5284, 0x04 }, { 0x5285, 0x00 }, { 0x5286, 0x04 },
	{ 0x5287, 0x00 }, { 0x5480, 0x04 }, { 0x5481, 0x00 }, { 0x5482, 0x04 }, { 0x5483, 0x00 },
	{ 0x5484, 0x04 }, { 0x5485, 0x00 }, { 0x5486, 0x04 }, { 0x5487, 0x00 }, { 0x5680, 0x04 },
	{ 0x5681, 0x00 }, { 0x5682, 0x04 }, { 0x5683, 0x00 }, { 0x5684, 0x04 }, { 0x5685, 0x00 },
	{ 0x5686, 0x04 }, { 0x5687, 0x00 }, { 0x5880, 0x04 }, { 0x5881, 0x00 }, { 0x5882, 0x04 },
	{ 0x5883, 0x00 }, { 0x5884, 0x04 }, { 0x5885, 0x00 }, { 0x5886, 0x04 }, { 0x5887, 0x00 },
	{ 0x52c6, 0x00 }, { 0x52c7, 0x12 }, { 0x52c8, 0x04 }, { 0x52c9, 0x02 }, { 0x52ca, 0x01 },
	{ 0x52cb, 0x01 }, { 0x52cc, 0x04 }, { 0x52cd, 0x02 }, { 0x52ce, 0x01 }, { 0x52cf, 0x01 },
	{ 0x52d0, 0x03 }, { 0x52d1, 0x08 }, { 0x52d2, 0x0c }, { 0x54c6, 0x00 }, { 0x54c7, 0x12 },
	{ 0x54c8, 0x04 }, { 0x54c9, 0x02 }, { 0x54ca, 0x01 }, { 0x54cb, 0x01 }, { 0x54cc, 0x04 },
	{ 0x54cd, 0x02 }, { 0x54ce, 0x01 }, { 0x54cf, 0x01 }, { 0x54d0, 0x03 }, { 0x54d1, 0x08 },
	{ 0x54d2, 0x0c }, { 0x56c6, 0x00 }, { 0x56c7, 0x12 }, { 0x56c8, 0x04 }, { 0x56c9, 0x02 },
	{ 0x56ca, 0x01 }, { 0x56cb, 0x01 }, { 0x56cc, 0x04 }, { 0x56cd, 0x02 }, { 0x56ce, 0x01 },
	{ 0x56cf, 0x01 }, { 0x56d0, 0x03 }, { 0x56d1, 0x08 }, { 0x56d2, 0x0c }, { 0x58c6, 0x00 },
	{ 0x58c7, 0x12 }, { 0x58c8, 0x04 }, { 0x58c9, 0x02 }, { 0x58ca, 0x01 }, { 0x58cb, 0x01 },
	{ 0x58cc, 0x04 }, { 0x58cd, 0x02 }, { 0x58ce, 0x01 }, { 0x58cf, 0x01 }, { 0x58d0, 0x03 },
	{ 0x58d1, 0x08 }, { 0x58d2, 0x0c }, { 0x5004, 0x1e }, { 0x610a, 0x07 }, { 0x610b, 0x80 },
	{ 0x610c, 0x05 }, { 0x610d, 0x00 }, { 0x6102, 0x3f }, { 0x6120, 0x75 }, { 0x6121, 0x75 },
	{ 0x6122, 0x75 }, { 0x6123, 0x75 }, { 0x6124, 0x75 }, { 0x6125, 0x75 }, { 0x6126, 0x75 },
	{ 0x6127, 0x75 }, { 0x6128, 0x75 }, { 0x6129, 0x75 }, { 0x612a, 0x75 }, { 0x612b, 0x75 },
	{ 0x612c, 0x75 }, { 0x612d, 0x75 }, { 0x612e, 0x75 }, { 0x612f, 0x75 }, { 0x6130, 0x75 },
	{ 0x6131, 0x75 }, { 0x6132, 0x75 }, { 0x6133, 0x75 }, { 0x6134, 0x75 }, { 0x6135, 0x75 },
	{ 0x6136, 0x75 }, { 0x6137, 0x75 }, { 0x6138, 0x75 }, { 0x6139, 0x75 }, { 0x613a, 0x75 },
	{ 0x613b, 0x75 }, { 0x613c, 0x75 }, { 0x613d, 0x75 }, { 0x613e, 0x75 }, { 0x613f, 0x75 },
	{ 0x6140, 0x75 }, { 0x6141, 0x75 }, { 0x6142, 0x75 }, { 0x6143, 0x75 }, { 0x6144, 0x75 },
	{ 0x6145, 0x75 }, { 0x6146, 0x75 }, { 0x6147, 0x75 }, { 0x6148, 0x75 }, { 0x6149, 0x75 },
	{ 0x614a, 0x75 }, { 0x614b, 0x75 }, { 0x614c, 0x75 }, { 0x614d, 0x75 }, { 0x614e, 0x75 },
	{ 0x614f, 0x75 }, { 0x6150, 0x75 }, { 0x6151, 0x75 }, { 0x6152, 0x75 }, { 0x6153, 0x75 },
	{ 0x6154, 0x75 }, { 0x6155, 0x75 }, { 0x6156, 0x75 }, { 0x6157, 0x75 }, { 0x6158, 0x75 },
	{ 0x6159, 0x75 }, { 0x615a, 0x75 }, { 0x615b, 0x75 }, { 0x615c, 0x75 }, { 0x615d, 0x75 },
	{ 0x615e, 0x75 }, { 0x615f, 0x75 }, { 0x6160, 0x75 }, { 0x6161, 0x75 }, { 0x6162, 0x75 },
	{ 0x6163, 0x75 }, { 0x6164, 0x75 }, { 0x6165, 0x75 }, { 0x6166, 0x75 }, { 0x6167, 0x75 },
	{ 0x6168, 0x75 }, { 0x6169, 0x75 }, { 0x616a, 0x75 }, { 0x616b, 0x75 }, { 0x616c, 0x75 },
	{ 0x616d, 0x75 }, { 0x616e, 0x75 }, { 0x616f, 0x75 }, { 0x6170, 0x75 }, { 0x6171, 0x75 },
	{ 0x6172, 0x75 }, { 0x6173, 0x75 }, { 0x6174, 0x75 }, { 0x6175, 0x75 }, { 0x6176, 0x75 },
	{ 0x6177, 0x75 }, { 0x6178, 0x75 }, { 0x6179, 0x75 }, { 0x617a, 0x75 }, { 0x617b, 0x75 },
	{ 0x617c, 0x75 }, { 0x617d, 0x75 }, { 0x617e, 0x75 }, { 0x617f, 0x75 }, { 0x6180, 0x75 },
	{ 0x6181, 0x75 }, { 0x6182, 0x75 }, { 0x6183, 0x75 }, { 0x6184, 0x75 }, { 0x6185, 0x75 },
	{ 0x6186, 0x75 }, { 0x6187, 0x75 }, { 0x6188, 0x75 }, { 0x6189, 0x75 }, { 0x618a, 0x75 },
	{ 0x618b, 0x75 }, { 0x618c, 0x75 }, { 0x618d, 0x75 }, { 0x618e, 0x75 }, { 0x618f, 0x75 },
	{ 0x6190, 0x75 }, { 0x6191, 0x75 }, { 0x6192, 0x75 }, { 0x6193, 0x75 }, { 0x6194, 0x75 },
	{ 0x6195, 0x75 }, { 0x6196, 0x75 }, { 0x6197, 0x75 }, { 0x6198, 0x75 }, { 0x6199, 0x75 },
	{ 0x619a, 0x75 }, { 0x619b, 0x75 }, { 0x619c, 0x75 }, { 0x619d, 0x75 }, { 0x619e, 0x75 },
	{ 0x619f, 0x75 }, { 0x61a0, 0x75 }, { 0x61a1, 0x75 }, { 0x61a2, 0x75 }, { 0x61a3, 0x75 },
	{ 0x61a4, 0x75 }, { 0x61a5, 0x75 }, { 0x61a6, 0x75 }, { 0x61a7, 0x75 }, { 0x61a8, 0x75 },
	{ 0x61a9, 0x75 }, { 0x61aa, 0x75 }, { 0x61ab, 0x75 }, { 0x61ac, 0x75 }, { 0x61ad, 0x75 },
	{ 0x61ae, 0x75 }, { 0x61af, 0x75 }, { 0x5d62, 0x07 }, { 0x5d40, 0x02 }, { 0x5d41, 0x01 },
	{ 0x5d63, 0x08 }, { 0x5d64, 0x01 }, { 0x5d65, 0xff }, { 0x5d56, 0x00 }, { 0x5d57, 0x20 },
	{ 0x5d58, 0x00 }, { 0x5d59, 0x20 }, { 0x5d5a, 0x00 }, { 0x5d5b, 0x0c }, { 0x5d5c, 0x02 },
	{ 0x5d5d, 0x40 }, { 0x5d5e, 0x02 }, { 0x5d5f, 0x40 }, { 0x5d60, 0x03 }, { 0x5d61, 0x40 },
	{ 0x5d4a, 0x02 }, { 0x5d4b, 0x40 }, { 0x5d4c, 0x02 }, { 0x5d4d, 0x40 }, { 0x5d4e, 0x02 },
	{ 0x5d4f, 0x40 }, { 0x5d50, 0x18 }, { 0x5d51, 0x80 }, { 0x5d52, 0x18 }, { 0x5d53, 0x80 },
	{ 0x5d54, 0x18 }, { 0x5d55, 0x80 }, { 0x5d46, 0x20 }, { 0x5d47, 0x00 }, { 0x5d48, 0x22 },
	{ 0x5d49, 0x00 }, { 0x5d42, 0x20 }, { 0x5d43, 0x00 }, { 0x5d44, 0x22 }, { 0x5d45, 0x00 },
	{ 0x4221, 0x07 }, { 0x380e, 0x02 }, { 0x380f, 0xae }, { 0x380c, 0x04 }, { 0x380d, 0x47 },
	{ 0x384c, 0x02 }, { 0x384d, 0x0d }, { 0x388c, 0x02 }, { 0x388d, 0x2b }, { 0x420e, 0x66 },
	{ 0x420f, 0x5d }, { 0x4210, 0xa8 }, { 0x4211, 0x55 }, { 0x507a, 0x5f }, { 0x507b, 0x46 },
	{ 0x0304, 0x01 }, { 0x0305, 0x2c }, { 0x0307, 0x02 }, { 0x4837, 0x1a }, { 0x420e, 0x54 },
	{ 0x420f, 0xa0 }, { 0x4210, 0xca }, { 0x4211, 0xf2 }, { 0x040e, 0x08 }, { 0x040c, 0x50 },
	{ 0x040d, 0xe2 }, { 0x0408, 0x70 }, { 0x0409, 0x56 }, { 0x040a, 0x2d }, { 0x040b, 0x09 },
	{ 0x0324, 0x00 }, { 0x0325, 0xf0 }, { 0x380c, 0x07 }, { 0x380d, 0xae }, { 0x384c, 0x03 },
	{ 0x384d, 0xd7 }, { 0x388c, 0x03 }, { 0x388d, 0xd7 }, { 0x0400, 0x70 }, { 0x0401, 0x80 },
	{ 0x0403, 0x2d }, { 0x0404, 0x09 }, { 0x0405, 0x25 }, { 0x0406, 0x80 }, { 0x0407, 0x08 },
	{ 0x4603, 0x11 }, { 0x0309, 0x02 }, { 0x0320, 0x02 }, { 0x0362, 0x8a },

	/* Omnivision settings */
	{ 0x0307, 0x03 }, { 0x4837, 0x1a }, { 0x040e, 0x08 },
	{ 0x040c, 0x47 }, { 0x040d, 0xed }, { 0x0408, 0x70 }, { 0x0409, 0x62 }, { 0x040a, 0x2d },
	{ 0x040b, 0x09 }, { 0x4603, 0x11 }, { 0x0324, 0x01 }, { 0x0325, 0x36 }, { 0x0329, 0x02 },
	{ 0x032a, 0x05 }, { 0x032b, 0x08 }, { 0x032c, 0x02 }, { 0x0327, 0x09 }, { 0x0326, 0x0e },
	{ 0x380e, 0x02 }, { 0x380f, 0xae }, { 0x380c, 0x05 }, { 0x380d, 0xe2 }, { 0x384c, 0x02 },
	{ 0x384d, 0xf1 }, { 0x388c, 0x02 }, { 0x388d, 0xf1 }, { 0x0400, 0x70 }, { 0x0401, 0x7f },
	{ 0x0403, 0x2d }, { 0x0404, 0x09 }, { 0x0405, 0x2b }, { 0x0406, 0x8d }, { 0x0407, 0x08 },

	/* Fsync */
	{ 0x3015, 0x0A }, { 0x3009, 0x02 }, { 0x3822, 0x24 }, { 0x3823, 0x50 }, { 0x383e, 0x81 },
	{ 0x3881, 0x34 }, { 0x3882, 0x02 }, { 0x3883, 0x8a }, { 0x388e, 0x01 }, { 0x388f, 0x00 },
	{ 0x3892, 0x44 }, { 0x3826, 0x00 },

	/* Declaring the registers to be included in the embedded data */
	{ 0x3208, 0x04 }, { 0x350e, 0x02 }, { 0x3514, 0x02 }, { 0x3518, 0x03 }, { 0x354e, 0x02 },
	{ 0x3554, 0x02 }, { 0x3558, 0x03 }, { 0x3594, 0x02 }, { 0x3598, 0x03 }, { 0x35ce, 0x02 },
	{ 0x35d4, 0x02 }, { 0x35d8, 0x03 }, { 0x483E, 0x02 }, { 0x4D2A, 0x02 }, { 0x5280, 0x08 },
	{ 0x5480, 0x08 }, { 0x5680, 0x08 }, { 0x5880, 0x0A }, { 0x3208, 0x14 },

	{ 0x431c, 0x6e }, { 0x0100, 0x00 },
};

static const struct regmap_range ox03c10_volatile_ranges[] = {
	{ 0x7057, 0x7059 }, { 0x705b, 0x705d }, /* OTP correction registers */

};

static const struct regmap_access_table ox03c10_volatile_access_table = {
	.yes_ranges = ox03c10_volatile_ranges,
	.n_yes_ranges = ARRAY_SIZE(ox03c10_volatile_ranges),
};

static const struct regmap_config ox03c10_sensor_regmap_cfg = {
	.name = "ox03c10",
	.reg_bits = 16,
	.val_bits = 8,

	.max_register = 0x705D,
	.volatile_table = &ox03c10_volatile_access_table,
	.cache_type = REGCACHE_RBTREE,
};

static struct ox03c10_mode ox03c10_modes[] = {
	{
		.width = OX03C10_PIXEL_ARRAY_WIDTH,
		.height = OX03C10_PIXEL_ARRAY_HEIGHT,
		.hts = 2186,
		.vts = 1372,
		.fps = 30
	},
};

static int ox03c10_exposure_set(struct ox03c10 *sensor, struct ox03c10_exposure *exp)
{
	int ret = 0;
	u8 buf[2];

	if (sensor->streaming)
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x00); /* set group hold 0 */

	buf[0] = (exp->dcg >> 8) & 0xff;
	buf[1] = exp->dcg & 0xff;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_HCG_CTRL_01, buf, 2);
	buf[0] = (exp->spd >> 8) & 0xff;
	buf[1] = exp->spd & 0xff;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_SPD_CTRL_01, buf, 2);
	buf[0] = (exp->vs >> 8) & 0xff;
	buf[1] = exp->vs & 0xff;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_VS_CTRL_01, buf, 2);

	if (sensor->streaming) {
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x10); /* end group hold 0 */
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0xE0); /* quick launch */
	}

	return ret ? -EIO : 0;
}

static int ox03c10_analogue_gain_set(struct ox03c10 *sensor, struct ox03c10_analog_gain *gain)
{
	int ret = 0;
	u8 buf[2];

	if (sensor->streaming)
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x01); /* set group hold 1 */

	buf[0] = (gain->hcg >> 4) & 0xf;
	buf[1] = (gain->hcg & 0xf) << 4;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_HCG_CTRL_08, buf, 2);
	buf[0] = (gain->spd >> 4) & 0xf;
	buf[1] = (gain->spd & 0xf) << 4;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_SPD_CTRL_08, buf, 2);
	buf[0] = (gain->lcg >> 4) & 0xf;
	buf[1] = (gain->lcg & 0xf) << 4;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_LCG_CTRL_08, buf, 2);
	buf[0] = (gain->vs >> 4) & 0xf;
	buf[1] = (gain->vs & 0xf) << 4;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_VS_CTRL_08, buf, 2);

	if (sensor->streaming) {
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x11); /* end group hold 1 */
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0xE1); /* quick launch */
	}

	return ret ? -EIO : 0;
}

static int ox03c10_digital_gain_set(struct ox03c10 *sensor, struct ox03c10_digital_gain *gain)
{
	int ret = 0;
	u8 buf[3];

	if (sensor->streaming)
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x02); /* set group hold 2 */

	buf[0] = (gain->hcg >> 10) & 0xf;
	buf[1] = (gain->hcg >> 2) & 0xff;
	buf[2] = (gain->hcg & 0x3) << 6;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_HCG_CTRL_0A, buf, 3);
	buf[0] = (gain->spd >> 10) & 0xf;
	buf[1] = (gain->spd >> 2) & 0xff;
	buf[2] = (gain->spd & 0x3) << 6;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_SPD_CTRL_0A, buf, 3);
	buf[0] = (gain->lcg >> 10) & 0xf;
	buf[1] = (gain->lcg >> 2) & 0xff;
	buf[2] = (gain->lcg & 0x3) << 6;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_LCG_CTRL_0A, buf, 3);
	buf[0] = (gain->vs >> 10) & 0xf;
	buf[1] = (gain->vs >> 2) & 0xff;
	buf[2] = (gain->vs & 0x3) << 6;
	ret |= regmap_bulk_write(sensor->rmap, OX03C10_AEC_VS_CTRL_0A, buf, 3);

	if (sensor->streaming) {
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x12); /* end group hold 2 */
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0xE2); /* quick launch */
	}

	return ret ? -EIO : 0;
}

static int ox03c10_wb_gain_set(struct ox03c10 *sensor, struct ox03c10_wb_capture_gain *wb_gain)
{
	int i, ret = 0;
	u8 buf[8];
	u16 base_addr[4] = {
		OX03C10_AWB_GAIN_HCG_0,
		OX03C10_AWB_GAIN_LCG_0,
		OX03C10_AWB_GAIN_SPD_0,
		OX03C10_AWB_GAIN_VS_0
	};

	if (sensor->streaming)
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x03); /* set group hold 3 */

	for (i = 0; i < 4; i++, wb_gain++) {
		buf[0] = (wb_gain->b >> 8) & 0xff;
		buf[1] = wb_gain->b & 0xff;
		buf[2] = (wb_gain->gb >> 8) & 0xff;
		buf[3] = wb_gain->gb & 0xff;
		buf[4] = (wb_gain->gr >> 8) & 0xff;
		buf[5] = wb_gain->gr & 0xff;
		buf[6] = (wb_gain->r >> 8) & 0xff;
		buf[7] = wb_gain->r & 0xff;

		ret |= regmap_bulk_write(sensor->rmap, base_addr[i], buf, 8);
	}

	if (sensor->streaming) {
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x13); /* end group hold 3 */
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0xE3); /* quick launch */
	}

	return ret ? -EIO : 0;
}

static int ox03c10_pwl_enable(struct ox03c10 *sensor, bool en)
{
	int ret;

	if (sensor->streaming)
		return -EBUSY;

	ret = regmap_update_bits(sensor->rmap, OX03C10_FORMAT_REG_1F, BIT(5), en ? BIT(5) : 0);
	ret |= regmap_update_bits(sensor->rmap, OX03C10_ISP_CTRL_02, BIT(6), en ? BIT(6) : 0);

	return ret ? -EIO : 0;
}

static int ox03c10_pwl_params_set(struct ox03c10 *sensor, struct ox03c10_pwl_ctrl *pwl_ctrl)
{
	if (sensor->streaming)
		return -EBUSY;

	return regmap_update_bits(sensor->rmap, OX03C10_FORMAT_REG_1F, 0xD8,
				  (pwl_ctrl->pack24bit_sel << 6) | (pwl_ctrl->pwl_mode << 3));
}

static int ox03c10_pwl_lut_set(struct ox03c10 *sensor, u8 *lut)
{
	if (sensor->streaming)
		return -EBUSY;

	return regmap_bulk_write(sensor->rmap, OX03C10_PWL0_0_1, lut, OX03C10_PWL_LUT_SIZE);
}

static int ox03c10_hflip_enable(struct ox03c10 *sensor, bool en)
{
	int ret;

	if (sensor->streaming)
		return -EBUSY;

	ret = regmap_update_bits(sensor->rmap, OX03C10_REG_WIN_09, BIT(0), BIT(0));
	ret |= regmap_update_bits(sensor->rmap, OX03C10_TIMING_CTRL_REG_20, BIT(5),
				  en ? 0 : BIT(5));

	return ret ? -EIO : 0;
}

static int ox03c10_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct ox03c10 *sensor = container_of(ctrl->handler, struct ox03c10, ctrl_handler);

	switch (ctrl->id) {
	case V4L2_CID_OX03C10_EXPOSURE:
		return ox03c10_exposure_set(sensor, ctrl->p_new.p);

	case V4L2_CID_OX03C10_ANALOGUE_GAIN:
		return ox03c10_analogue_gain_set(sensor, ctrl->p_new.p);

	case V4L2_CID_OX03C10_DIGITAL_GAIN:
		return ox03c10_digital_gain_set(sensor, ctrl->p_new.p);

	case V4L2_CID_OX03C10_WB_GAIN:
		return ox03c10_wb_gain_set(sensor, ctrl->p_new.p);

	case V4L2_CID_OX03C10_PWL_EN:
		return ox03c10_pwl_enable(sensor, ctrl->val);

	case V4L2_CID_OX03C10_PWL_CTRL:
		return ox03c10_pwl_params_set(sensor, ctrl->p_new.p);

	case V4L2_CID_OX03C10_PWL_KNEE_POINTS_LUT:
		return ox03c10_pwl_lut_set(sensor, ctrl->p_new.p);

	case V4L2_CID_HFLIP:
		return ox03c10_hflip_enable(sensor, ctrl->val);

	default:
		return -EINVAL;
	}
}

static int ox03c10_otp_correction_get(struct ox03c10 *sensor, void *ret_values)
{
	struct ox03c10_otp_correction *otp = ret_values;
	int ret;
	u8 reg_val[3] = {0};

	ret = regmap_bulk_read(sensor->rmap, 0x7057, reg_val, 3);
	otp->val1 = (reg_val[0] << 16) | (reg_val[1] << 8) | reg_val[2];
	ret |= regmap_bulk_read(sensor->rmap, 0x705b, reg_val, 3);
	otp->val2 = (reg_val[0] << 16) | (reg_val[1] << 8) | reg_val[2];

	return ret ? -EIO : 0;
}

static int ox03c10_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
	struct ox03c10 *sensor = container_of(ctrl->handler, struct ox03c10, ctrl_handler);

	switch (ctrl->id) {
	case V4L2_CID_OX03C10_OTP_CORRECTION:
		return ox03c10_otp_correction_get(sensor, ctrl->p_new.p);

	default:
		return -EINVAL;
	}
}

static const struct v4l2_ctrl_ops ox03c10_ctrl_ops = {
	.g_volatile_ctrl = ox03c10_g_volatile_ctrl,
	.s_ctrl		 = ox03c10_s_ctrl,
};

static void ox03c10_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
{
	/* no logging yet */
}

static struct ox03c10_exposure ox03c10_initial_exposure;
static struct ox03c10_analog_gain ox03c10_initial_analog_gain;
static struct ox03c10_digital_gain ox03c10_initial_digital_gain;
static struct ox03c10_wb_capture_gain ox03c10_initial_wb_capture_gain[4];
static struct ox03c10_pwl_ctrl ox03c10_initial_pwl_ctrl;
static u8 ox03c10_initial_pwl_knee_points_lut[OX03C10_PWL_LUT_SIZE];

static int ox03c10_get_initial_params(struct ox03c10 *sensor)
{
	u16 wb_base_addr[4] = {
		OX03C10_AWB_GAIN_HCG_0,
		OX03C10_AWB_GAIN_LCG_0,
		OX03C10_AWB_GAIN_SPD_0,
		OX03C10_AWB_GAIN_VS_0,
	};
	int ret = 0;
	u8 buf[8];
	int i;

	/* get initial exposure */
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_HCG_CTRL_01, buf, 2);
	ox03c10_initial_exposure.dcg = buf[0] << 8 | buf[1];
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_SPD_CTRL_01, buf, 2);
	ox03c10_initial_exposure.spd = buf[0] << 8 | buf[1];
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_VS_CTRL_01, buf, 2);
	ox03c10_initial_exposure.vs = buf[0] << 8 | buf[1];

	/* get initial analog gains */
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_HCG_CTRL_08, buf, 2);
	ox03c10_initial_analog_gain.hcg = ((buf[0] & 0xf) << 4) | ((buf[1] & 0xf0) >> 4);
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_SPD_CTRL_08, buf, 2);
	ox03c10_initial_analog_gain.spd = ((buf[0] & 0xf) << 4) | ((buf[1] & 0xf0) >> 4);
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_LCG_CTRL_08, buf, 2);
	ox03c10_initial_analog_gain.lcg = ((buf[0] & 0xf) << 4) | ((buf[1] & 0xf0) >> 4);
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_VS_CTRL_08, buf, 2);
	ox03c10_initial_analog_gain.vs = ((buf[0] & 0xf) << 4) | ((buf[1] & 0xf0) >> 4);

	/* get initial digital gains */
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_HCG_CTRL_0A, buf, 3);
	ox03c10_initial_digital_gain.hcg = ((buf[0] & 0xf) << 10) | (buf[1] << 2) |
					   ((buf[2] & 0x3) >> 6);
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_SPD_CTRL_0A, buf, 3);
	ox03c10_initial_digital_gain.spd = ((buf[0] & 0xf) << 10) | (buf[1] << 2) |
					   ((buf[2] & 0x3) >> 6);
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_LCG_CTRL_0A, buf, 3);
	ox03c10_initial_digital_gain.lcg = ((buf[0] & 0xf) << 10) | (buf[1] << 2) |
					   ((buf[2] & 0x3) >> 6);
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_AEC_VS_CTRL_0A, buf, 3);
	ox03c10_initial_digital_gain.vs = ((buf[0] & 0xf) << 10) | (buf[1] << 2) |
					  ((buf[2] & 0x3) >> 6);

	/* get initial white balance settings */
	for (i = 0; i < 4; i++) {
		ret |= regmap_bulk_read(sensor->rmap, wb_base_addr[i], buf, 8);
		ox03c10_initial_wb_capture_gain[i].b  = (buf[0] << 8) | buf[1];
		ox03c10_initial_wb_capture_gain[i].gb = (buf[2] << 8) | buf[3];
		ox03c10_initial_wb_capture_gain[i].gr = (buf[4] << 8) | buf[5];
		ox03c10_initial_wb_capture_gain[i].r  = (buf[6] << 8) | buf[7];
	}

	/* get initial PWL control params */
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_FORMAT_REG_1F, buf, 1);
	ox03c10_initial_pwl_ctrl.pack24bit_sel = (buf[0] & 0xc0) >> 6;
	ox03c10_initial_pwl_ctrl.pwl_mode = (buf[0] & 0x18) >> 3;

	/* get initial PWL knee points LUT */
	ret |= regmap_bulk_read(sensor->rmap, OX03C10_PWL0_0_1,
				ox03c10_initial_pwl_knee_points_lut, OX03C10_PWL_LUT_SIZE);

	return ret ? -EIO : 0;
}

static void ox03c10_v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
					   union v4l2_ctrl_ptr ptr)
{
	u32 tot_elems = ctrl->elems;
	u32 elems = tot_elems - from_idx;

	if (from_idx >= elems)
		return;

	switch (ctrl->id) {
	case V4L2_CID_OX03C10_EXPOSURE:
		memcpy(ptr.p, &ox03c10_initial_exposure, sizeof(ox03c10_initial_exposure));
		break;

	case V4L2_CID_OX03C10_ANALOGUE_GAIN:
		memcpy(ptr.p, &ox03c10_initial_analog_gain, sizeof(ox03c10_initial_analog_gain));
		break;

	case V4L2_CID_OX03C10_DIGITAL_GAIN:
		memcpy(ptr.p, &ox03c10_initial_digital_gain, sizeof(ox03c10_initial_digital_gain));
		break;

	case V4L2_CID_OX03C10_WB_GAIN:
		memcpy(ptr.p, &ox03c10_initial_wb_capture_gain,
		       sizeof(ox03c10_initial_wb_capture_gain));
		break;

	case V4L2_CID_OX03C10_PWL_CTRL:
		memcpy(ptr.p, &ox03c10_initial_pwl_ctrl, sizeof(ox03c10_initial_pwl_ctrl));
		break;

	case V4L2_CID_OX03C10_PWL_KNEE_POINTS_LUT:
		memcpy(ptr.p, &ox03c10_initial_pwl_knee_points_lut,
		       sizeof(ox03c10_initial_pwl_knee_points_lut));
		break;

	default:
		v4l2_ctrl_type_op_init(ctrl, from_idx, ptr);
		break;
	}
}

static const struct v4l2_ctrl_type_ops ox03c10_ctrl_type_ops = {
	.init		= ox03c10_v4l2_ctrl_type_op_init,
	.validate	= v4l2_ctrl_type_op_validate,
	.equal		= v4l2_ctrl_type_op_equal,
	.log		= ox03c10_ctrl_type_op_log,
};

static const struct v4l2_ctrl_config ox03c10_ctrl_cfgs[] = {
	{
		.ops		= &ox03c10_ctrl_ops,
		.type_ops	= &ox03c10_ctrl_type_ops,
		.id		= V4L2_CID_OX03C10_EXPOSURE,
		.name		= "Exposure for: DCG, SPD, VS",
		.type		= V4L2_CTRL_TYPE_U8,
		.min		= 0x00,
		.max		= 0xff,
		.step		= 1,
		.def		= 0,
		.dims		= { sizeof(struct ox03c10_exposure) },
	},
	{
		.ops		= &ox03c10_ctrl_ops,
		.type_ops	= &ox03c10_ctrl_type_ops,
		.id		= V4L2_CID_OX03C10_ANALOGUE_GAIN,
		.name		= "Analog gains for: HCG, LCG, SPD, VS",
		.type		= V4L2_CTRL_TYPE_U8,
		.min		= 0x00,
		.max		= 0xff,
		.step		= 1,
		.def		= 0x0,
		.dims		= { sizeof(struct ox03c10_analog_gain) },
	},
	{
		.ops		= &ox03c10_ctrl_ops,
		.type_ops	= &ox03c10_ctrl_type_ops,
		.id		= V4L2_CID_OX03C10_DIGITAL_GAIN,
		.name		= "Digital gains for: HCG, LCG, SPD, VS",
		.type		= V4L2_CTRL_TYPE_U8,
		.min		= 0x00,
		.max		= 0xff,
		.step		= 1,
		.def		= 0x00,
		.dims		= { sizeof(struct ox03c10_digital_gain) },
	},
	{
		.ops		= &ox03c10_ctrl_ops,
		.type_ops	= &ox03c10_ctrl_type_ops,
		.id		= V4L2_CID_OX03C10_WB_GAIN,
		.name		= "White balance gain for: HCG, LCG, SPD, VS",
		.type		= V4L2_CTRL_TYPE_U8,
		.min		= 0x00,
		.max		= 0xff,
		.step		= 1,
		.def		= 0x00,
		.dims		= { 4 * sizeof(struct ox03c10_wb_capture_gain) },
	},
	{
		.ops		= &ox03c10_ctrl_ops,
		.id		= V4L2_CID_OX03C10_PWL_EN,
		.name		= "Enable PWL compression",
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.min		= false,
		.max		= true,
		.step		= 1,
		.def		= true,
	},
	{
		.ops		= &ox03c10_ctrl_ops,
		.type_ops	= &ox03c10_ctrl_type_ops,
		.id		= V4L2_CID_OX03C10_PWL_CTRL,
		.name		= "PWL compression control params",
		.type		= V4L2_CTRL_TYPE_U8,
		.min		= 0x00,
		.max		= 0xff,
		.step		= 1,
		.def		= 0,
		.dims		= { sizeof(struct ox03c10_pwl_ctrl) }
	},
	{
		.ops		= &ox03c10_ctrl_ops,
		.type_ops	= &ox03c10_ctrl_type_ops,
		.id		= V4L2_CID_OX03C10_PWL_KNEE_POINTS_LUT,
		.name		= "PWL knee points LUT",
		.type		= V4L2_CTRL_TYPE_U8,
		.min		= 0x00,
		.max		= 0xff,
		.step		= 1,
		.def		= 0,
		.dims		= { 132 }
	},
	{
		.ops		= &ox03c10_ctrl_ops,
		.type_ops	= &ox03c10_ctrl_type_ops,
		.id		= V4L2_CID_OX03C10_OTP_CORRECTION,
		.name		= "OTP correction values",
		.type		= V4L2_CTRL_TYPE_U8,
		.min		= 0x00,
		.max		= 0xff,
		.step		= 1,
		.def		= 0,
		.dims		= { sizeof(struct ox03c10_otp_correction) },
		.flags		= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
	},
};

int ox03c10_v4l2_controls_init(struct ox03c10 *sensor)
{
	struct device *dev = &sensor->client->dev;
	struct v4l2_ctrl_handler *ctrl_handler = &sensor->ctrl_handler;
	struct v4l2_fwnode_device_properties props;
	int i;
	u16 hblank, vblank;
	int ret;

	ret = v4l2_ctrl_handler_init(&sensor->ctrl_handler, ARRAY_SIZE(ox03c10_ctrl_cfgs) + 4);
	if (ret < 0) {
		dev_err(dev, "Cannot initialize V4L2 ctrl handler.\n");
		return ret;
	}

	v4l2_ctrl_new_std(ctrl_handler, &ox03c10_ctrl_ops, V4L2_CID_PIXEL_RATE,
			  OX03C10_PIXEL_RATE, OX03C10_PIXEL_RATE, 1, OX03C10_PIXEL_RATE);

	hblank = sensor->cur_mode->hts - sensor->cur_mode->width;

	v4l2_ctrl_new_std(ctrl_handler, &ox03c10_ctrl_ops, V4L2_CID_HBLANK,
			  hblank, hblank, 1, hblank);

	vblank = sensor->cur_mode->vts - sensor->cur_mode->height;

	v4l2_ctrl_new_std(ctrl_handler, &ox03c10_ctrl_ops, V4L2_CID_VBLANK,
			  vblank, vblank, 1, vblank);

	v4l2_ctrl_new_std(ctrl_handler, &ox03c10_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 0);

	v4l2_ctrl_new_std(ctrl_handler, &ox03c10_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);

	ret = v4l2_fwnode_device_parse(sensor->dev, &props);
	if (ret)
		goto free_ctrls;

	ret = v4l2_ctrl_new_fwnode_properties(ctrl_handler, &ox03c10_ctrl_ops, &props);
	if (ret)
		goto free_ctrls;

	for (i = 0; i < ARRAY_SIZE(ox03c10_ctrl_cfgs); i++) {
		sensor->ctrls[i] = v4l2_ctrl_new_custom(ctrl_handler, &ox03c10_ctrl_cfgs[i], NULL);
		if (ctrl_handler->error) {
			dev_err(sensor->dev, "Adding control (%d) failed: %d\n",
				i, ctrl_handler->error);
			ret = ctrl_handler->error;
			goto free_ctrls;
		}
	}

	return 0;

free_ctrls:
	v4l2_ctrl_handler_free(ctrl_handler);
	return ret;
}
EXPORT_SYMBOL(ox03c10_v4l2_controls_init);

int ox03c10_streaming_start(struct ox03c10 *sensor, bool start)
{
	int ret;

	if (!start) {
		/*
		 * For stopping, we need to use group hold registers in order to be able to
		 * stop during vertical blanking to avoid MIPI issues.
		 */
		ret = regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x00);
		ret |= regmap_write(sensor->rmap, OX03C10_SMIA_R0100, 0);
		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0x10);

		ret |= regmap_write(sensor->rmap, OX03C10_GRP_HOLD_8, 0xA0);

		/* Wait a maximum of 1 time frame. Worst case is 33.33ms. */
		msleep(34);
	} else {
		ret = regmap_write(sensor->rmap, OX03C10_SMIA_R0100, 1);
	}

	sensor->streaming = start;

	return ret ? -EIO : 0;
}
EXPORT_SYMBOL(ox03c10_streaming_start);

static int ox03c10_sensor_init(struct ox03c10 *sensor)
{
	const struct ox03c10_reg *reg;
	int ret = 0;
	int i;

	/* software reset */
	regmap_write(sensor->rmap, OX03C10_SMIA_R0103, 1);
	regmap_write(sensor->rmap, OX03C10_SMIA_R0107, 1);

	usleep_range(100, 200);

	for (i = 0; i < ARRAY_SIZE(ox03c10_init_data); i++) {
		reg = &ox03c10_init_data[i];

		/*
		 * Re-enable the cache after the embedded data registers ranges have been set.
		 */
		if (reg->addr == OX03C10_GRP_HOLD_8 && (reg->val == 0x14 || reg->val == 0x15))
			regcache_cache_bypass(sensor->rmap, false);

		ret = regmap_write(sensor->rmap, reg->addr, reg->val);
		if (ret < 0) {
			dev_err(&sensor->client->dev, "Failed to write addr 0x%04x with 0x%02x\n",
				reg->addr, reg->val);
			return ret;
		}

		/*
		 * Make sure we bypass the cache when setting address ranges for embedded data.
		 * Otherwise, our cache will hold a range instead of the actual value...
		 */
		if (reg->addr == OX03C10_GRP_HOLD_8 && (reg->val == 0x04 || reg->val == 0x05))
			regcache_cache_bypass(sensor->rmap, true);
	}

	sensor->cur_mode = &ox03c10_modes[0];

	return ox03c10_get_initial_params(sensor);
}

struct ox03c10 *ox03c10_init_with_dummy_client(struct i2c_client *client,
					       bool use_dummy)
{
	struct device *dev = &client->dev;
	struct ox03c10 *sensor;
	int ret;

	sensor = devm_kzalloc(dev, sizeof(*sensor) +
				ARRAY_SIZE(ox03c10_ctrl_cfgs) * sizeof(struct v4l2_ctrl *),
				GFP_KERNEL);
	if (!sensor)
		return ERR_PTR(-ENOMEM);

	sensor->dev = dev;

	if (use_dummy) {
		sensor->client =
			devm_i2c_new_dummy_device(dev, client->adapter,
						  OX03C10_I2C_ADDR);
		if (IS_ERR(sensor->client))
			return ERR_PTR(-ENODEV);
	} else {
		sensor->client = client;
	}

	sensor->rmap = devm_regmap_init_i2c(sensor->client, &ox03c10_sensor_regmap_cfg);
	if (IS_ERR(sensor->rmap)) {
		ret = PTR_ERR(sensor->rmap);
		dev_err(dev, "Failed to allocate sensor register map: %d\n", ret);
		goto error;
	}

	ret = ox03c10_sensor_init(sensor);
	if (ret)
		goto error;

	return sensor;

error:
	return ERR_PTR(ret);
}
EXPORT_SYMBOL(ox03c10_init_with_dummy_client);

struct v4l2_ctrl_handler *ox03c10_ctrl_handler_get(struct ox03c10 *sensor)
{
	return &sensor->ctrl_handler;
}
EXPORT_SYMBOL(ox03c10_ctrl_handler_get);

void ox03c10_ctrl_handler_free(struct ox03c10 *sensor)
{
	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
}
EXPORT_SYMBOL(ox03c10_ctrl_handler_free);

struct ox03c10_mode *ox03c10_get_mode(int index)
{
	if (index >= ARRAY_SIZE(ox03c10_modes))
		return ERR_PTR(-EINVAL);

	return &ox03c10_modes[index];
}
EXPORT_SYMBOL(ox03c10_get_mode);

MODULE_DESCRIPTION("Omnivision OX03C10 sensor library");
MODULE_AUTHOR("Laurentiu Palcu");
MODULE_LICENSE("GPL");
